﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Framework.Event;
using System.Reflection;
using Framework.Infrastructure;

namespace Framework.Bus
{
    class HandlerMeta
    {
        public HandlerMeta(Type handlerType, MethodInfo handleMethod)
        {
            HandlerType = handlerType;
            HandleMethod = handleMethod;
        }
        public Type HandlerType { get; set; }
        public MethodInfo HandleMethod { get; set; }

        public void Handle(object message, object executor = null)
        {
            if (executor == null)
            {
                executor = IoCFactory.Resolve(HandlerType);
            }
            HandleMethod.Invoke(executor, new object[] { message });
        }
    }

    public class MessageDispatcher : IMessageDispatcher
    {
        private readonly Dictionary<Type, List<HandlerMeta>> _DispatcherHandlerMetas;
        private readonly Hashtable discardKeyTypes;
        public MessageDispatcher()
        {
            _DispatcherHandlerMetas = new Dictionary<Type, List<HandlerMeta>>();
            discardKeyTypes = new Hashtable();
        }

        public void ClearRegistration()
        {
            _DispatcherHandlerMetas.Clear();
        }

       
        public void DispatchMessage(object message, object executor = null)
        {
            var keyType = message.GetType();
            if (_DispatcherHandlerMetas.ContainsKey(keyType))
            {
                var dispatcherHandlers = _DispatcherHandlerMetas[keyType];
                if (dispatcherHandlers != null && dispatcherHandlers.Count > 0)
                {
                    foreach (var dispatcherHandler in dispatcherHandlers.Where(h => executor == null ||
                            h.HandlerType.IsAssignableFrom(executor.GetType())))
                    {
                        dispatcherHandler.Handle(message, executor);
                    }
                }
            }
            else if (!discardKeyTypes.Contains(keyType))
            {
                bool isDiscardKeyTypes = true;
                foreach (var dispatcherHandlers in _DispatcherHandlerMetas)
                {
                    if (keyType.IsSubclassOf(dispatcherHandlers.Key))
                    {
                        var messageDispatcherHandlers = _DispatcherHandlerMetas[dispatcherHandlers.Key];
                        if (messageDispatcherHandlers != null && messageDispatcherHandlers.Count > 0)
                        {
                            foreach (var handler in messageDispatcherHandlers)
                            {
                                handler.Handle(message, executor);
                            }
                            isDiscardKeyTypes = false;
                            _DispatcherHandlerMetas.Add(keyType, messageDispatcherHandlers);
                            break;
                        }
                    }
                }
                if (isDiscardKeyTypes)
                {
                    discardKeyTypes.Add(keyType, keyType);
                }
            }
        }


        public void Register(Type keyType, Type handlerType, MethodInfo handle)
        {
            var handlerMeta = new HandlerMeta(handlerType, handle);
            if (_DispatcherHandlerMetas.ContainsKey(keyType))
            {
                List<HandlerMeta> registeredDispatcherHandlers = _DispatcherHandlerMetas[keyType];
                if (registeredDispatcherHandlers != null)
                {
                    if (!registeredDispatcherHandlers.Contains(handlerMeta))
                        registeredDispatcherHandlers.Add(handlerMeta);
                }
                else
                {
                    registeredDispatcherHandlers = new List<HandlerMeta>();
                    _DispatcherHandlerMetas[keyType] = registeredDispatcherHandlers;
                    registeredDispatcherHandlers.Add(handlerMeta);
                }
            }
            else
            {
                var registeredDispatcherHandlers = new List<HandlerMeta>();
                registeredDispatcherHandlers.Add(handlerMeta);
                _DispatcherHandlerMetas.Add(keyType, registeredDispatcherHandlers);
            }
        }
    }
}
